﻿using System;
using System.Collections.Generic;
using System.Drawing;
using BGShell.Components;
using BGShell.Drawing;

namespace BGShell.PowerShell.DataTips
{
    internal abstract class TypeModelDataTip : DataTipItem, IComparer<DataTipItem>
    {
        private Type _type;
        private Boolean _valueIsCaughtException;

        protected TypeModelDataTip(String name, Object value, Boolean valueIsCaughtException, Type type)
            : base(name, value)
        {
            _type = type;
            _valueIsCaughtException = valueIsCaughtException;
        }

        public TypeModelIcon Icon
        {
            get;
            set;
        }

        public TypeModelIconOverlay IconOverlay
        {
            get;
            set;
        }

        public virtual Type ValueType
        {
            get
            {
                if (Value != null)
                {
                    return Value.GetType();
                }

                return _type;
            }
        }

        public override bool HasIcon
        {
            get { return Icon != TypeModelIcon.None; }
        }

        public override string ValueAsString
        {
            get
            {
                if (_valueIsCaughtException)
                {
                    return String.Format(System.Globalization.CultureInfo.InvariantCulture,
                        Resources.ExceptionRetrievingProperty, Name, Value.GetType().FullName);
                }

                var result = base.ValueAsString;

                if ((Value != null) && (ValueType != null))
                {
                    if (ValueType.IsArray)
                    {
                        var array = (Array)(Value);
                        var typeName = PSLanguage.FormatTypeName(ValueType.GetElementType());

                        return typeName + '[' + PSLanguage.FormatNumber(array.Length) + ']';
                    }

                    if (StringComparer.Ordinal.Equals(result, ValueType.FullName))
                    {
                        result = PSLanguage.FormatTypeName(ValueType);
                    }
                }

                return result;
            }
        }

        public override void PaintIcon(Graphics graphics, Rectangle rect)
        {
            TypeModelIconRenderer.RenderIcon(graphics, rect, Icon, IconOverlay);
        }


        public virtual int Compare(DataTipItem x, DataTipItem y)
        {
            return StringComparer.InvariantCultureIgnoreCase.Compare(x.Name, y.Name);
        }

        protected override void OnCreateSubitems()
        {
            InnerList.Clear();

            if (!IsPrimitiveType(ValueType))
            {
                if ((Value != null) && (ValueType != null) && (ValueType.IsArray))
                {
                    InnerList.AddRange(CreateArrayItems((Array)(Value)));
                }
                else
                {
                    InnerList.AddRange(MemberSubItems());
                    InnerList.Sort(this);
                }

                InnerList.InsertRange(0, PrependSubItems());
                InnerList.AddRange(AppendSubItems());
            }

            base.OnCreateSubitems();
        }

        protected virtual void SortMembers()
        {
            
        }

        protected abstract TypeModelDataTip CreateArrayItem(String name, Object value, Type elementType);

        protected virtual Boolean IsPrimitiveType(Type t)
        {
            return (t == null) || t.IsPrimitive || t.IsEnum || (t == typeof(String));
        }

        protected virtual IEnumerable<DataTipItem> AppendSubItems()
        {
            return NoItems;
        }

        protected virtual IEnumerable<DataTipItem> MemberSubItems()
        {
            return NoItems;
        }

        protected virtual IEnumerable<DataTipItem> PrependSubItems()
        {
            return NoItems;
        }

        protected DataTipItem[] CreateArrayItems(Array array)
        {
            var elementType = array.GetType().GetElementType();
            var items = new List<DataTipItem>();

            if (array.Rank == 1)
            {
                for (int i = 0; i < array.Length; i++)
                {
                    var name = '[' + PSLanguage.FormatNumber(i) + ']';
                    var value = array.GetValue(i);

                    var item = CreateArrayItem(name, value, elementType);

                    item.Icon = TypeModelIcon.Field;
                    item.IconOverlay = TypeModelIconOverlay.None;

                    items.Add(item);
                }
            }

            return items.ToArray();
        }


        protected static readonly DataTipItem[] NoItems = new DataTipItem[0];
    }
}
